#include "RecordModule.h"

static unsigned char fileBuf[32];

const unsigned short recPlugin[40] = { /* Compressed plugin for recording*/
    0x0007, 0x0001, 0x8010, 0x0006, 0x001c, 0x3e12, 0xb817, 0x3e14, /*    0 */
    0xf812, 0x3e01, 0xb811, 0x0007, 0x9717, 0x0020, 0xffd2, 0x0030, /*    8 */
    0x11d1, 0x3111, 0x8024, 0x3704, 0xc024, 0x3b81, 0x8024, 0x3101, /*   10 */
    0x8024, 0x3b81, 0x8024, 0x3f04, 0xc024, 0x2808, 0x4800, 0x36f1, /*   18 */
    0x9811, 0x0007, 0x0001, 0x8028, 0x0006, 0x0002, 0x2a00, 0x040e,
};

/* PCM file Header */
unsigned char pcmHeader[44] = {
    'R', 'I', 'F', 'F',
    0xFF, 0xFF, 0xFF, 0xFF,
    'W', 'A', 'V', 'E',
    'f', 'm', 't', ' ',
    0x10, 0, 0, 0,          /* 16 */
    0x1, 0,                 /* PCM */
    0x1, 0,                 /* chan */
    0x00, 0x00, 0x40, 0x1F,   /* sampleRate */
    0x00, 0x00, 0x3E, 0x80,   /* byteRate */
    2, 0,                   /* blockAlign */
    0x10, 0,                /* bitsPerSample */
    'd', 'a', 't', 'a',
    0xFF, 0xFF, 0xFF, 0xFF
};

void RecordModule::init()
{
    SPI.setClockDivider(SPI_CLOCK_DIV2);

    pinMode(VS_XRESET, OUTPUT);
    pinMode(VS_DREQ, INPUT);
    pinMode(VS_XDCS, OUTPUT);
    pinMode(VS_XCS, OUTPUT);

    digitalWrite(VS_XDCS, HIGH);
    digitalWrite(VS_XCS, HIGH);

    reset();
}

void RecordModule::record(char* recfile)
{
    unsigned long fileSize = 0;
    unsigned int n;

    if (SD.exists(recfile))
    {
        SD.remove(recfile);
    }

    File file = SD.open(recfile, FILE_WRITE);

    initRecord();

    bool flag = true;
    while (state == RECORD_DOING)
    {
        unsigned char *rbp = fileBuf;
        if ((n = readRegister(SPI_HDAT1)) > 0)
        {
            flag = true;
            //Make little-endian conversion for 16-bit PCM .WAV files 
            if (n > 16) n = 16;
            for (char i = 0; i<n; i++) {
                unsigned int w = readRegister(SPI_HDAT0);
                *rbp++ = (unsigned char)(w & 0xFF);
                *rbp++ = (unsigned char)(w >> 8);
            }
            SPI.setClockDivider(SPI_CLOCK_DIV2);
            file.write(fileBuf, 2 * n);
            SPI.setClockDivider(SPI_CLOCK_DIV4);
            fileSize += 2 * n;
        }
    }

    /* Update file sizes for an RIFF PCM .WAV file */
    file.seek(0);
    setHeaderField(pcmHeader + 4, fileSize - 8);
    setHeaderField(pcmHeader + 40, fileSize - 36);
    file.write(pcmHeader, sizeof(pcmHeader));
    file.close();

    /* Finally, reset the VS10xx software, including realoading the
    patches package, to make sure everything is set up properly. */
    softReset();
}

bool RecordModule::accept(ModuleRequest & request)
{
    RecordModuleRequest& recordRequest = (RecordModuleRequest& ) request;
    this->accept(recordRequest);
}

void RecordModule::accept(RecordModuleRequest & request)
{

}

bool RecordModule::checkAlive()
{
    return false;
}

void RecordModule::stop()
{
    state = RECORD_CLOSE;
}

// reset for vs10xx
void RecordModule::reset(void)
{
    putInReset();
    delay(2); //it is a must,2ms
              /* Send dummy SPI byte to initialize atmel SPI */
              ////SPIPutCharWithoutWaiting(0xFF);
    SPI.transfer(0xff);
    deselectControlBus();
    deselectDataBus();
    releaseFromReset();
    while (!readDREQ());

    softReset(); //vs10xx soft reset.
}

/** Soft Reset of VS10xx (Between songs) */
void RecordModule::softReset(void)
{
    SPI.setClockDivider(SPI_CLOCK_DIV8);

    /* Soft Reset of VS10xx */
    writeRegister(SPI_MODE, 0x0804); /* Newmode, Reset, No L1-2 */

    while (!readDREQ());

    writeRegister(SPI_HDAT0, 0xABAD);
    writeRegister(SPI_HDAT1, 0x1DEA);

    uint16_t hd0 = readRegister(SPI_HDAT0);
    uint16_t hd1 = readRegister(SPI_HDAT1);
    if (hd0 != 0xABAD || hd1 != 0x1DEA) {
        Serial.printf("Something wrong with VS10xx: %04X - %04X\n", hd0, hd1);
    }
    else
    {
        Serial.println("reset success");
    }

    writeRegister(SPI_CLOCKF, 0x9800);   //Set the clock
    writeRegister(SPI_AUDATA, 0xbb81);   //samplerate 48k,stereo
    writeRegister(SPI_BASS, 0x0055);    //set accent
    writeRegister(SPI_VOL, 0x4040);     //Set volume level

    while (!readDREQ());

    SPI.setClockDivider(SPI_CLOCK_DIV4);
}

void RecordModule::writeRegister(unsigned char addressbyte, unsigned int value)
{
    deselectDataBus();
    while (!readDREQ());
    selectControlBus();
    SPI.transfer(VS_WRITE_COMMAND);
    SPI.transfer(addressbyte);
    SPI.transfer(value >> 8);
    SPI.transfer(value & 0xFF);
    while (!readDREQ());
    deselectControlBus();
}

unsigned int RecordModule::readRegister(unsigned char addressbyte)
{
    unsigned int result = 0;
    deselectDataBus();
    while (!readDREQ());
    selectControlBus(); //XCS = 0
    SPI.transfer(VS_READ_COMMAND); //send read command
    SPI.transfer(addressbyte);    //send register address
    result = SPI.transfer(0xff) << 8;
    result |= SPI.transfer(0xff);
    while (!readDREQ());
    deselectControlBus();
    return result;
}

void RecordModule::writeData(unsigned char *databuf, unsigned char datalen)
{
    selectDataBus();
    while (!readDREQ());
    while (datalen--)
    {
        SPI.transfer(*databuf++);
    }
    deselectDataBus();
}

void RecordModule::initRecord()
{
    SPI.setClockDivider(SPI_CLOCK_DIV16);

    writeRegister(SPI_BASS, 0x0055);    //set accent
    writeRegister(SPI_AICTRL0, 8000);    //samplerate 8k
    writeRegister(SPI_AICTRL1, 0);       //record gain
    writeRegister(SPI_AICTRL2, 0);       //Set the gain maximum,65536=64X
    writeRegister(SPI_AICTRL3, 6);       //PCM Mode ,left channel
    writeRegister(SPI_CLOCKF, 0x2000);   //set clock

    writeRegister(SPI_MODE, 0x1804);   //mic,Initialize recording
    vTaskDelay(10 / portTICK_RATE_MS);

    loadPlugin(recPlugin, 40);
    vTaskDelay(10 / portTICK_RATE_MS);

    SPI.setClockDivider(SPI_CLOCK_DIV2);
}

/*
**@ function name: loadPlugin
**@ usage:load a software patch for vs10xx
**@ input:none
**@ retval:none
*/
void RecordModule::loadPlugin(const unsigned short *plugin, int length)
{
    int i = 0;
    while (i<length) {
        unsigned short addr, n, val;
        addr = plugin[i++];
        n = plugin[i++];
        if (n & 0x8000U) { //RLE run, replicate n samples 
            n &= 0x7FFF;
            val = plugin[i++];
            while (n--) {
                writeRegister(addr, val);
            }
        }
        else {			 //Copy run, copy n samples 
            while (n--) {
                val = plugin[i++];
                writeRegister(addr, val);
            }
        }
    }
}
/****************The end***********************************************/